Lambda
只有 SAM接口(函数式接口【类中只有一个抽象方法的接口,可以有其他成员,但是抽象方法只有一个】)才能使用 Lambda 表达式,Lambda 表达式的作用就是简化代码,使代码更整洁。
函数式接口
现在已学的 SAM 接口:
- java.lang.Runnable: public void run()
- java.lang.Comparable: public int conpareTo(T t)
- java.lang.Comparable: public int conpare(T t1, T t2)
- java.lang.Iterable: public Iterator iterator()
- java.lang.reflect.InvocationHandler: public Object invoke(Object proxy, Method method, Object[] args)
- java.io.FileFilter: public boolean accept(File. pathname)
1.8 中建议将符合 SAM 的接口都加上一个 @FunctionalInterface 注解,以上 SAM 接口中如果没有 @FunctionalInterface 注解的,表示以后可能会增加第二个抽象方法,变成非 SAM 接口。这种接口谨慎使用 Lambda 表达式。
JDK 1.8 增加了 java.util.function 包,里面的接口可以分为四大类。包含了大部分可能会遇到的函数式接口。
- 消费型接口 (Consumer),这类接口它的抽象方法有参数但是无返回值。
- 供给型接口 (Supplier),这类接口它的抽象方法无参数但是有返回值。
- 判断型接口 (Predicate),这类接口它的抽象方法有参数,返回值类型为 boolean。
- 功能型接口 (Function),这类接口它的抽象方法有参数有返回值。
Lambda 语法
Lambda 表达式式给函数式接口的形参或变量赋值用的,语法格式为:
(形参列表) 就是函数式接口的抽象方法的形参列表。
如果形参列表是空参,() 是不能省略的。
如果形参列表非空,并且类型是确定的,那么形参的数据类型是可以省略的。
如果形参列表非空,并且只有一个形参,并且数据类型也省略了,那么 () 是可以省略的,如果数据类型没有省略, () 也不能省略。
{lambda 体} 就是函数式接口的抽象方法的方法体。
- 如果 lambda体 不止一个语句,那么 {} 不能省略,并且每一个语句都要以 ; 结束。
- 如果 lambda体 只有一个语句,那么 {}; 可以省略。
- 如果该 lambda体 的返回值不是 void,那么lambda体 里面要有 return 语句。如果只有一个返回语句时,{} 和 return 可以省略。
练习1
使用 forEach(Consumer c) 方法遍历 Collection 系列的集合。Collection 自带的 forEach 接收的参数是一个 Consumer,我们只要记住 Consumer 的特征就能写出 lambda 表达式,具体的 Consumer 中的方法是不需要关注的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.itguigu.lambda;
import java.util.ArrayList;
import org.junit.Test;
public class TestLambda { @Test public void test1() { ArrayList<String> userList = new ArrayList<>(); userList.add("zhangsan"); userList.add("lisi"); userList.add("wangwu"); userList.add("zhaoliu"); userList.forEach((String t) -> {System.out.println(t);}); userList.forEach(t -> System.out.println(t)); } }
|
练习2
创建一个 ArrayList 集合,保存学生成员 Student,使用 removeIf(Predicate p) 方法删除成绩不及格的学生。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| package com.itguigu.lambda;
import java.util.ArrayList;
import org.junit.Test;
public class TestLamda2 { @Test public void test1(){ ArrayList<Student> studentList = new ArrayList<>(); studentList.add(new Student("1", "zhangsan", 99)); studentList.add(new Student("1", "lisi", 59)); studentList.add(new Student("1", "wangwu", 22)); studentList.removeIf((Student s) -> {return (s.getScore() < 60);}); studentList.forEach((Student s) -> {System.out.println(s);}); studentList.removeIf(s -> (s.getScore() < 60)); studentList.forEach(s -> System.out.println(s)); } }
class Student { private String id; private String name; private int score; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", score=" + score + "]"; } public Student(String id, String name, int score) { super(); this.id = id; this.name = name; this.score = score; } public Student() { super(); } }
|
练习3
使用 TreeSet 保存学生对象,默认按照学号进行排序,然后在对成绩进行排序,成绩一样在根据学号排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| package com.itguigu.lambda;
import java.util.TreeSet; import org.junit.Test;
public class TestLamda3 { @Test public void test2() { TreeSet<EnglishStudent> englishStudentList = new TreeSet<>((EnglishStudent e1, EnglishStudent e2) -> {return e1.getScore() - e2.getScore();}) ; englishStudentList.add(new EnglishStudent(1, "zhangsan", 99)); englishStudentList.add(new EnglishStudent(2, "lisi", 89)); englishStudentList.add(new EnglishStudent(3, "wangwu", 99)); englishStudentList.forEach(f-> System.out.println(f)); System.out.println("test2");
TreeSet<EnglishStudent> englishStudentList2 = new TreeSet<>((e1, e2) -> e1.getScore() == e2.getScore()?e1.getId() - e2.getId(): e1.getScore() - e2.getScore()) ; englishStudentList2.add(new EnglishStudent(1, "zhangsan", 99)); englishStudentList2.add(new EnglishStudent(2, "lisi", 89)); englishStudentList2.add(new EnglishStudent(3, "wangwu", 99)); englishStudentList2.forEach(f-> System.out.println(f));
} @Test public void test1() { TreeSet<EnglishStudent> englishStudentList = new TreeSet<>(); englishStudentList.add(new EnglishStudent(1, "zhangsan", 99)); englishStudentList.add(new EnglishStudent(2, "lisi", 89)); englishStudentList.add(new EnglishStudent(3, "wangwu", 99)); englishStudentList.forEach(f-> System.out.println(f)); System.out.println("test1");
} }
class EnglishStudent implements Comparable<EnglishStudent>{ private int id; private String name; private int score; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } public EnglishStudent(int id, String name, int score) { super(); this.id = id; this.name = name; this.score = score; } public EnglishStudent() { super(); } @Override public int compareTo(EnglishStudent o) { return this.id - o.id; } @Override public String toString() { return "EnglishStudent [id=" + id + ", name=" + name + ", score=" + score + "]"; } }
|
练习4
创建一个 HashMap,键和值都是 String 类型,使用 Map 的 void forEach(BiConsumer<? super K, ? super V> action) 这个方法遍历显示 Map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.itguigu.lambda;
import java.util.HashMap;
import org.junit.Test;
public class TestLamb4 { @Test public void test() { HashMap<String, String> map = new HashMap<>(); map.put("zhangsan", "xxx"); map.put("lisi", "yyy"); map.put("wangwu", "zzz"); map.forEach((String k, String v) -> {System.out.println(k + ":" + v);}); map.forEach((k, v) -> System.out.println(k + ":" + v)); } }
|
练习5
声明一个 Book 图书类,属性有编号,书名,作者,价格。创建一个 BookService 管理类,里面有一个集合 ArrayList<Book> 用来存储书籍。现要求在 BookService 中创建一个 query 方法 public ArrayList<Book> query(Predicate<Book> p){} 可以实现以下功能:
- 查询指定 id 的图书
- 查询某个作者的图书
- 查询某个价格范围的图书
- 查询书名包含某个字的图书
- 查询所有的图书
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| package com.itguigu.lambda;
import java.util.ArrayList; import java.util.function.Predicate;
public class TestLambda5 { public static void main(String[] args) { BookService bookService = new BookService(); ArrayList<Book> query = bookService.query((Book b) -> {return (b.getId() == 1);}); query.forEach(b -> System.out.println(b)); System.out.println("============ 1 ============"); ArrayList<Book> query2 = bookService.query(b -> b.getAuthor() == "罗贯中"); query2.forEach(b -> System.out.println(b)); System.out.println("============ 2 ============"); ArrayList<Book> query3 = bookService.query(b -> b.getPrice() > 10); query3.forEach(b -> System.out.println(b)); System.out.println("============ 3 ============"); ArrayList<Book> query4 = bookService.query(b -> b.getName().contains("三")); query4.forEach(b -> System.out.println(b)); System.out.println("============ 4 ============"); ArrayList<Book> query5 = bookService.query(b -> true); query5.forEach(b -> System.out.println(b)); System.out.println("============ 5 ============");
} }
class BookService { private ArrayList<Book> BookList; public BookService() { BookList = new ArrayList<>(); BookList.add(new Book(1, "三国演义", "罗贯中", 99.0)); BookList.add(new Book(2, "西游记", "吴承恩", 88.0)); BookList.add(new Book(3, "红楼梦", "曹雪芹", 22.0)); } public ArrayList<Book> query(Predicate<Book> predicate){ ArrayList<Book> resultList = new ArrayList<>(); for (Book book : BookList) { if (predicate.test(book)) { resultList.add(book); } } return resultList; } }
class Book{ private int id; private String name; private String author; private Double price; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } @Override public String toString() { return "Book [id=" + id + ", name=" + name + ", author=" + author + ", price=" + price + "]"; } public Book(int id, String name, String author, Double price) { super(); this.id = id; this.name = name; this.author = author; this.price = price; } }
|
方法引用和构造器引用
当 lambda 表达式的 lambda体满足一些情况时,可以使用方法引用和构造器引用再次简化。
- 当 lambda 体是通过调用一个先有的类,对象的先有的方法来完成功能时。
- 并且这个方法的形参列表和返回值类型与该 lambda 表达式所赋值的函数式接口的抽象方法的形参列表和返回值类型对应。
形式:
- 对象::实例方法名
- 类名::静态方法名
- 类名::实例方法名
- 类名::new —> 构造器引用
- 数组类型::new. —> 特殊的构造器引用,数组的构造器引。用
举例,对象::实例方法名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package com.itguigu.lambda;
import java.util.ArrayList;
import org.junit.Test;
public class TestLambda { @Test public void test1() { ArrayList<String> userList = new ArrayList<>(); userList.add("zhangsan"); userList.add("lisi"); userList.add("wangwu"); userList.add("zhaoliu"); userList.forEach((String t) -> {System.out.println(t);}); userList.forEach(t -> System.out.println(t));
userList.forEach(System.out::println); } }
|
Stream API
Stream 的特点:1. Stream 本身不负责存储数据,只负责处理数据。 2. Stream 不会改变原对象,每次处理都会返回一个新的 Stream 3. Stream 操作是延迟的,只有到需要结果的时候才会真正的执行。
操作 Stream 的步骤
1. 建立一个 Stream 数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| package com.itguigu.lambda;
import java.util.ArrayList; import java.util.Arrays; import java.util.stream.Stream;
import org.junit.Test;
public class TestStream { @Test public void test1() { String[] arr = {"hello", "test"}; Stream<String> stream = Arrays.stream(arr); stream.forEach(t->System.out.println(t)); } @Test public void test2() { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("zhangsan"); arrayList.add("lisi"); Stream<String> stream = arrayList.stream(); stream.forEach(t->System.out.println(t)); } @Test public void test3() { Stream<String> stream = Stream.of("zhangsan", "lisi", "wangwu"); stream.forEach(t->System.out.println(t)); } @Test public void test4() { Stream<Double> stream = Stream.generate(() -> Math.random()); stream.forEach(t->System.out.println(t)); } @Test public void test5() {
Stream<Integer> stream = Stream.iterate(1, t -> t+2); stream.forEach(t->System.out.println(t)); } }
|
2. 处理数据(中间过程)
filter(Predicate t) 按照 p 的条件进行过滤,Predicate 接口为判断形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| package com.itguigu.lambda;
import java.util.ArrayList; import java.util.stream.Stream;
import org.junit.Test;
public class TestFilter { @Test public void test1() { ArrayList<Employee> arrayList = new ArrayList<>(); arrayList.add(new Employee("zhangsan", 1)); arrayList.add(new Employee("lisi", 2)); arrayList.add(new Employee("wangwu", 3));
Stream<Employee> stream = arrayList.stream(); Stream<Employee> filter = stream.filter((t) -> t.getId() >= 2); filter.forEach(System.out::println); } }
class Employee{ private String name; private int id; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Employee other = (Employee) obj; if (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } public Employee(String name, int id) { super(); this.name = name; this.id = id; } @Override public String toString() { return "Employee [name=" + name + ", id=" + id + "]"; } }
|
distinct 去重
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| package com.itguigu.lambda;
import java.util.ArrayList; import java.util.stream.Stream;
import org.junit.Test;
public class TestFilter { @Test public void test1() { ArrayList<Employee> arrayList = new ArrayList<>(); arrayList.add(new Employee("zhangsan", 1)); arrayList.add(new Employee("zhangsan", 1)); arrayList.add(new Employee("wangwu", 3));
Stream<Employee> stream = arrayList.stream(); Stream<Employee> filter = stream.distinct(); filter.forEach(System.out::println); } }
class Employee{ private String name; private int id; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Employee other = (Employee) obj; if (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } public Employee(String name, int id) { super(); this.name = name; this.id = id; } @Override public String toString() { return "Employee [name=" + name + ", id=" + id + "]"; } }
|
limit(long Maxsize) 去流中的前 Maxsize 个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.itguigu.lambda;
import java.util.Arrays; import java.util.stream.IntStream;
import org.junit.Test;
public class TestFilter { @Test public void test1() { int[] arr = {1,2,3,4,5,6,7,8}; IntStream stream = Arrays.stream(arr); IntStream limit = stream.limit(3); limit.forEach(System.out::println); IntStream stream2 = Arrays.stream(arr); stream2.limit(3).forEach(System.out::println); } }
|
skip(long n) 跳过前 n 个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.itguigu.lambda;
import java.util.Arrays; import java.util.stream.IntStream;
import org.junit.Test;
public class TestFilter { @Test public void test1() { int[] arr = {1,2,3,4,5,6,7,8}; IntStream stream = Arrays.stream(arr); IntStream limit = stream.skip(3); limit.forEach(System.out::println); IntStream stream2 = Arrays.stream(arr); stream2.skip(3).forEach(System.out::println); } }
|
peek(Consumer action) 对流中的数据执行 Consumer 接口的 action 操作,Consumer 接口为消费形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.itguigu.lambda;
import java.util.Arrays; import java.util.stream.IntStream;
import org.junit.Test;
public class TestFilter { @Test public void test1() { int[] arr = {1,2,3,4,5,6,7,8}; IntStream stream = Arrays.stream(arr); IntStream peek = stream.peek((t) -> System.out.println(t)); long count = peek.count(); System.out.println(count); } }
|
sorted(Comparator com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.itguigu.lambda;
import java.util.stream.Stream;
import org.junit.Test;
public class TestFilter { @Test public void test1() { Stream.of(1,2,3,4,5,6,7,8,8,024,2424,65,456) .sorted((t1, t2) -> Integer.compare(t2, t1)) .forEach(System.out::println); Stream.of(1,2,3,4,5,6,7,8,8,024,2424,65,456) .sorted() .forEach(System.out::println); } }
|
map(Function f) 对流中的每一个元素,都映射 f 指定的操作,Function 接口为功能形,有参有返回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.itguigu.lambda;
import java.util.stream.Stream;
import org.junit.Test;
public class TestFilter { @Test public void test1() { Stream.of(1,2,3,4,5,6,7,8,8,024,2424,65,456) .map(t -> t + 2). forEach(System.out::println); } }
|
flatMap(Function f)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.itguigu.lambda;
import java.util.stream.Stream;
import org.junit.Test;
public class TestFilter { @Test public void test1() { Stream<String> stream = Stream.of("zhangsan,lisi,wangwu", "xiaoer,renren"); Stream<String> flatMap = stream.flatMap(t -> Stream.of(t.split(","))); flatMap.forEach(System.out::println);
} }
|
3. 取结果(终结)
forEach, count, max【求最大值】, min【求最小值】, allMatch【是否所有的都匹配】, anyMatch【是否有一个匹配】, noneMatch【是否都不匹配】, collect 【】等操作等会终结流。一旦流被终结就不能再使用了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.itguigu.lambda;
import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream;
import org.junit.Test;
public class TestFilter { @Test public void test1() { Optional<Integer> max = Stream.of(2,456,567,678,24,1,13,13).max((t1, t2) -> Integer.compare(t1, t2)); System.out.println(max); boolean allMatch = Stream.of(2,456,567,678,24,1,13,13).allMatch(t -> t%2==0); System.out.println(allMatch); List<Integer> collect = Stream.of(2,456,567,678,24,1,13,13).filter(t -> t > 5).collect(Collectors.toList()); System.out.println(collect); } }
|